package cqrs

import (
	"errors"
	"fmt"

	"github.com/ThreeDotsLabs/watermill/message"
)

// CommandEventMarshaler marshals Commands and Events to Watermill's messages and vice versa.
// Payload of the command needs to be marshaled to []bytes.
type CommandEventMarshaler interface {
	// Marshal marshals Command or Event to Watermill's message.
	Marshal(v interface{}) (*message.Message, error)

	// Unmarshal unmarshals watermill's message to v Command or Event.
	Unmarshal(msg *message.Message, v interface{}) (err error)

	// Name returns the name of Command or Event.
	// Name is used to determine, that received command or event is event which we want to handle.
	Name(v interface{}) string

	// NameFromMessage returns the name of Command or Event from Watermill's message (generated by Marshal).
	//
	// When we have Command or Event marshaled to Watermill's message,
	// we should use NameFromMessage instead of Name to avoid unnecessary unmarshaling.
	NameFromMessage(msg *message.Message) string
}

// CommandEventMarshalerDecorator decorates CommandEventMarshaler with additional functionality.
// It can be used to add additional metadata to the message.
type CommandEventMarshalerDecorator struct {
	CommandEventMarshaler

	// DecorateFunc is called after marshaling the message.
	DecorateFunc func(v any, msg *message.Message) error
}

// Marshal marshals Command or Event to Watermill's message and decorates it.
func (c CommandEventMarshalerDecorator) Marshal(v any) (*message.Message, error) {
	msg, err := c.CommandEventMarshaler.Marshal(v)
	if err != nil {
		return nil, err
	}

	if c.DecorateFunc == nil {
		return nil, errors.New("DecorateFunc is nil")
	}

	if err := c.DecorateFunc(v, msg); err != nil {
		return nil, fmt.Errorf("cannot decorate message: %w", err)
	}

	return msg, nil
}
